#Copyright 2009-2010 by Ward, David J ( pymethius@ominian.net )
import simplejson, random, time, functools
from uuid import uuid4
import logging
log = logging.getLogger(__name__)
import weakref

from twisted.internet import defer

#from twisted.protocols.basic import NetstringReceiver
from webmud.lib.basic import NetstringReceiver
from webmud.util import IoC

#TODO - Relcate to util or somewhere, anywhere, but here
def makeId(hexLen = 12):
    return "%s.%s" % (time.time(), uuid4().hex )

#TODO - RELOCATE and re-read decorators examples, something isn't right here
class TrapExceptions(object):
    
    def __init__(self, logObj):        
        self.logObj = logObj
        
    def __call__(self, func, *args, **kws):    
        @functools.wraps(func)
        def wrapper(*args, **kws):
            try:
                return func(*args, **kws)
            except:                
                self.logObj.exception("%s(%s,%s) " % ( func, args, kws, ))        
        return wrapper
    
    

#class WMProtocol(Protocol):    
class WMProtocol(NetstringReceiver):
    connection_count = 0
    
    def connectionMade(self):
        """
            TODO - There is way to much internal construction going on up in here.
            Ideally almost all of the assignments made here should be redelegated to the Factory and emulate
            a IoC pattern so that with the exception of conneciton_count, this class instance is nearly stateless
        """
        log.info("New connection %s:%s" % (self.transport.getPeer().host, self.transport.getPeer().port,) )
        self.connection_count += 1
        
        self.peerPort , selfpeerHost = self.transport.getPeer().port, self.transport.getPeer().host
        #Adding weak reference calls to the environ dictionary should help cut down on memory leaks and ghosts
        self.environ['protocol'] = weakref.proxy(self)
        self.closed = False
        
        #Get the ball rolling by directing for the Auth JS module to be loaded        
        self.sendRequest("loader", "auth")
        
    
    @TrapExceptions(log)
    def connectionLost(self, reason):
        log.debug("Lost connection from %s %s" % (self.peerPort, reason))
        self.closed = True
        #need to watch this closely for possible memory leaks        
        del(self.environ) 
    
        
    def write(self, msg):
        """
            Last method before execution path descends into the Super class and sends message
        """
        if self.closed:
            raise Exception("Corrupted user environ")
        log.debug("write(self(%s), %s)" % (self.peerPort, msg))
        if not isinstance(msg, str):
            msg = simplejson.dumps(msg)
            
        self.sendString(msg)
    
    @TrapExceptions(log)    
    def stringReceived(self, msg):
        
        log.info("Processing %s" % msg)
        try:
            message = simplejson.loads(msg)            
        except Exception:
            log.exception("Failed to parse incoming message: %s" % msg)            
            self.sendError("Syntax error", 500)
        else:
            self.factory.routeMessage(message, self.environ)
        
    @TrapExceptions(log)               
    def sendRequest(self, _method, _params, _id = None):
        if _id is None: _id = makeId()
        log.debug("Sending Request %s(%s) - %s " %(_method, _params, _id))
        self.write(dict(method=_method, params=_params, id=_id))
    
    @TrapExceptions(log)    
    def sendResponse(self, _result, _id ):
        log.debug("Sending response %s:%s" %(_result, _id))
        self.write({"result":_result, "id":_id})
        
    @TrapExceptions(log)
    def sendError(self, _error, _code = 500, _id = None):
        log.debug("Sending error %s:%s %s" % (_error,_code, _id) )
        self.write({"error":{"code":_code, "message":_error},"id":_id})
            
    def makeRequest(self, command, params, callback = None, onError = None):
        id = makeId()
        d = self.responseHandler.register(id)
        log.debug("Build Request with callbacks %s(%s) - %s" %(command, params, id))
        
        if callable(callback):
            d.addCallback(callback, msg_id = id)
        if callable(onError):
            d.addErrback(onError)
                    
        self.sendRequest(command, params, id)
        return d
             
             
        
        
            
                    
                 